/*
 *   Copyright 2012-present OSBI Ltd
 *
 *   Licensed under the Apache License, Version 2.0 (the "License");
 *   you may not use this file except in compliance with the License.
 *   You may obtain a copy of the License at
 *
 *       http://www.apache.org/licenses/LICENSE-2.0
 *
 *   Unless required by applicable law or agreed to in writing, software
 *   distributed under the License is distributed on an "AS IS" BASIS,
 *   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *   See the License for the specific language governing permissions and
 *   limitations under the License.
 */

// Packages
import React, { Component } from 'react';
import PropTypes from 'prop-types';
import $ from 'jquery';
import 'jquery-ui/ui/widgets/sortable.js';
import { defer, isEmpty } from 'lodash';
import {
  Alignment,
  Button,
  Card,
  Menu,
  MenuDivider,
  MenuItem,
  Popover,
  Position
} from '@blueprintjs/core';
import classNames from 'classnames';
import TruncateString from 'react-truncate-string';

// Utils
import { Saiku } from '../../../../utils';

// Styles
import './AxisMeasures.css';

class AxisMeasures extends Component {
  componentDidMount() {
    this.makeSortable();
  }

  componentWillUnmount() {
    if (this.$rootNode) {
      this.$rootNode.sortable('destroy');
    }
  }

  makeSortable() {
    const { onDragEnd } = this.props;

    defer(() => {
      this.$rootNode = $(this.rootNode).find('.sku-connectable');
      const type = this.$rootNode.attr('type');
      const destination = {
        droppableId: this.$rootNode.attr('dropdestinationid')
      };

      this.$rootNode.sortable({
        containment: $('.sku-query-designer-dialog'),
        items: '> li',
        opacity: 0.6,
        placeholder: 'placeholder',
        tolerance: 'pointer',
        start: (event, ui) => {
          ui.placeholder.text(ui.helper.text());
        },
        receive: (event, ui) => {
          // This event is triggered when an item from a connected sortable list
          // has been dropped into another list. The latter is the event target.
          // Source: http://api.jqueryui.com/sortable/#event-receive

          ui.item.draggable('disable');
        },
        stop: (event, ui) => {
          const $measureItem = ui.item.hasClass('ui-draggable')
            ? ui.item.find('.sku-measure-data')
            : ui.item;
          const source = {
            draggableId: $measureItem.attr('dragsourceid'),
            index: Number($measureItem.attr('dragsourceindex'))
          };
          const measure = {
            name: $measureItem.attr('measure'),
            type: $measureItem.attr('type')
          };
          const measureGroup = $measureItem.attr('measuregroup');
          const newIndex = ui.item.index();

          destination.index = newIndex;

          // Remove element from parent inside of sortable
          if (ui.item.hasClass('ui-draggable') || destination.index === -1) {
            ui.item.remove();
          }

          onDragEnd({
            type,
            data: measure,
            source,
            destination,
            options: { data: measureGroup }
          });
        }
      });
    });
  }

  hasDetails(itemDetails) {
    const { measuresDetails } = this.props;

    return (
      `${measuresDetails.location}_${measuresDetails.axis}` === itemDetails
    );
  }

  hasMeasures = () => isEmpty(this.props.measures);

  handleRemoveMeasure = event => {
    const { removeMeasure } = this.props;
    const measureName = event.target.getAttribute('measure');
    const source = {
      draggableId: event.target.getAttribute('dragsourceid'),
      index: event.target.getAttribute('dragsourceindex')
    };

    removeMeasure(measureName, source);
  };

  handleSetMeasuresDetails = event => {
    const { setMeasuresDetails } = this.props;
    const details = event.currentTarget.getAttribute('details');

    setMeasuresDetails(details);
  };

  handleClearAxis = () => {
    const { droppableId, clearAxis } = this.props;
    const axis = droppableId;

    clearAxis(axis);
  };

  renderMenuItems() {
    return (
      <Menu>
        <MenuItem
          details="BOTTOM_COLUMNS"
          text="Columns | Measures"
          active={this.hasDetails('BOTTOM_COLUMNS')}
          onClick={this.handleSetMeasuresDetails}
        />
        <MenuItem
          details="TOP_COLUMNS"
          text="Measures | Columns"
          active={this.hasDetails('TOP_COLUMNS')}
          onClick={this.handleSetMeasuresDetails}
        />
        <MenuItem
          details="BOTTOM_ROWS"
          text="Rows | Measures"
          active={this.hasDetails('BOTTOM_ROWS')}
          onClick={this.handleSetMeasuresDetails}
        />
        <MenuItem
          details="TOP_ROWS"
          text="Measures | Rows"
          active={this.hasDetails('TOP_ROWS')}
          onClick={this.handleSetMeasuresDetails}
        />
        <MenuDivider />
        <MenuItem
          details="reset"
          text="Reset Default"
          onClick={this.handleSetMeasuresDetails}
        />
        <MenuItem
          text="Clear Axis"
          onClick={this.handleClearAxis}
          disabled={this.hasMeasures()}
        />
        <MenuItem text="Cancel" />
      </Menu>
    );
  }

  renderMeasuresItems() {
    const { droppableId, measures } = this.props;

    return measures.map((measure, index) => (
      <li
        key={Saiku.uid(measure.name)}
        className="sku-measure-item"
        dragsourceid={droppableId}
        dragsourceindex={index}
        measure={measure.name}
        type={measure.type}
        onClick={this.handleRemoveMeasure}
        style={{ opacity: '1', width: '220px', height: '24px' }}
      >
        <TruncateString
          dragsourceid={droppableId}
          dragsourceindex={index}
          measure={measure.name}
          text={measure.name}
        />
      </li>
    ));
  }

  render() {
    const { type, droppableId, title, axisClassName } = this.props;

    return (
      <Card title={title}>
        <div className="sku-card-header">
          <Popover
            content={this.renderMenuItems()}
            position={Position.BOTTOM}
            minimal
            autoFocus={false}
            enforceFocus={false}
          >
            <Button
              className="sku-fields-list"
              text={title}
              alignText={Alignment.LEFT}
              rightIcon="caret-down"
              fill
            />
          </Popover>
        </div>
        <div className="sku-card-body" ref={node => (this.rootNode = node)}>
          <ul
            className={classNames(axisClassName, 'sku-connectable')}
            type={type}
            dropdestinationid={droppableId}
          >
            {this.renderMeasuresItems()}
          </ul>
        </div>
      </Card>
    );
  }
}

AxisMeasures.propTypes = {
  type: PropTypes.string.isRequired,
  droppableId: PropTypes.string.isRequired,
  title: PropTypes.string.isRequired,
  axisClassName: PropTypes.string.isRequired,
  measures: PropTypes.array,
  measuresDetails: PropTypes.object,
  onDragEnd: PropTypes.func.isRequired,
  removeMeasure: PropTypes.func.isRequired,
  setMeasuresDetails: PropTypes.func.isRequired,
  clearAxis: PropTypes.func.isRequired
};

export default AxisMeasures;
